#ifndef __ISCENENODE_H__
#define __ISCENENODE_H__

#include "CTypes.h"
#include "IMaterial.h"
#include "IMatrix3D.h"
#include "IDrawableFaceManager.h"
#include "UsefulData.h"
#include "IPosition.h"
#include "IScale.h"
#include "IRotation.h"
#include "IBoundigBox.h"
#include "ISceneNodeAnimator.h"
#include "ILog.h"
#include "../Config.h"

typedef struct RegistrationDataStrc
{
	IDrawableFaceManager *dem;
	u32 Time;
	matrix3dvpos *ParentAbsoluteMatrix;
}RegistrationDataStrc;

#ifdef __COMPILE_WIN32__
#pragma warning(disable: 4251)
#endif

class ISceneNode : public IPosition, public IScale, public IRotation
{
private:
	stringc Name;
protected:
	IListContainer<ISceneNode> ChildList;
	IListContainer<ISceneNodeAnimator> AnimatorList;
	ISceneNode *Parent;

	matrix3dvpos AbsoluteTransformation;
	matrix3dvpos RelativeTransformation;
	bool Visible;
	s32 id;

	virtual void CloneAttributes(ISceneNode *Source)
	{
		Position = Source->Position;
		Scale = Source->Scale;
		Rotation = Source->Rotation;
	}

public:
	ISceneNode(ISceneNodeBasicParamsDeclaration):
	  IPosition(Pos),IScale(scal),IRotation(rot),Parent(parent),AbsoluteTransformation(MCF_IDENTITY),id(id),Visible(true)
	{
		RelativeTransformation.MakeIdentity();

		if (Parent)
			SetParent(Parent);
		else
			Parent = NULL;

        Name = "NoNamed";
	}

	virtual ~ISceneNode()
	{
		DestroyNode();
	}

	virtual void DestroyNode()
	{
		AnimatorList.DeleteListNodes();
		ChildList.DeleteListNodes();
		SetParent(NULL);
	}

	virtual bool DestroyChild(ISceneNode *NodeToDestroy,bool Recursive=true)
	{
		bool Maked = ChildList.DeleteNode(NodeToDestroy);
		if (!Maked && Recursive)
		{
			for (std::list<ISceneNode*>::iterator it=ChildList.begin();it!=ChildList.end();it++)
			{
				if ((*it)->DestroyChild(NodeToDestroy,true))
					return true;
			}
		}

		return false;
	}

	inline void AddAnimator(ISceneNodeAnimator *Animator)	{	AnimatorList.push_back(Animator);	}

	inline void SetName(stringc NewName)			{	Name = NewName;				}
	inline s8* GetName()							{	return (s8*)Name.c_str();	}

	inline ISceneNode *GetParent()					{	return Parent;				}
	inline std::list<ISceneNode*> &GetChilds()		{	return ChildList;			}

	virtual matrix3dvpos &GetRelativeTransformation()
	{
		RelativeTransformation.MakeIdentity();
		RelativeTransformation.SetTranslation(Position);

		if (Rotation != vector3dvpos(0.f,0.f,0.f))
		{
			RelativeTransformation.SetRotationDegrees(Rotation);
		}

		if (Scale != vector3dvpos(1.f,1.f,1.f))
		{
			matrix3dvpos smat;
			smat.MakeIdentity();
			smat.SetScale(Scale);
			RelativeTransformation *= smat;
		}

		return RelativeTransformation;
	}

	inline bool GetVisible()						{	return Visible;				}
	inline void SetVisible(bool nv)					{	Visible = nv;				}

	inline void RemoveFromParentListChild()
	{
		if (Parent)
		{
			if (Parent->ChildList.size()>0)
			{
				for (std::list<ISceneNode*>::iterator it=Parent->ChildList.begin();it!=Parent->ChildList.end();it++)
				{
					if (*it==this)
					{
						Parent->ChildList.erase(it);
						return;
					}
				}
			}
		}
	}

	inline void SetParent(ISceneNode *newParent)
	{
		if (Parent)
			RemoveFromParentListChild();

		if (newParent)
		{
			Parent = newParent;
			newParent->AddChild(this);
		}
	}

	inline void AddChild(ISceneNode *newChild)
	{
		if (newChild)
		{
			ChildList.push_back(newChild);
			newChild->Parent = this;
		}
	}

	virtual void Sort()
	{
        for (std::list<ISceneNode*>::iterator it=ChildList.begin();it!=ChildList.end();it++)
        {
            for (std::list<ISceneNode*>::iterator it2=ChildList.begin();it2!=ChildList.end();it2++)
            {

                if ((*it2)->GetPositionZ() < (*it)->GetPositionZ())
                {
                    ISceneNode *Temp = (*it);
                    (*it) = (*it2);
                    (*it2) = Temp;
                }
            }
        }

        for (std::list<ISceneNode*>::iterator it=ChildList.begin();it!=ChildList.end();it++)
        {
            (*it)->Sort();
        }
	}

	virtual void UpdateNode()						= 0;
	virtual void Render(IDrawableFaceManager *dfm)	= 0;
	virtual void OnRender()							= 0;

	inline u32 RegisterNode(RegistrationDataStrc *rd)
	{
		u32 NodesRendered = 0;

		if (Visible)
		{
			NodesRendered++;

			for (std::list<ISceneNodeAnimator*>::iterator it=AnimatorList.begin();it!=AnimatorList.end();)
			{
				std::list<ISceneNodeAnimator*>::iterator it2= it;
				it2++;
				ISceneNodeAnimator *Anim = (*it);
				bool Destroy = Anim->AnimateNode(rd->Time,this);
				// If the result is "true" we must destroy the animator.
				if (Destroy)
				{
					AnimatorList.erase(it);
					delete Anim;
				}
				it = it2;
			}

			OnRender();
			Render(rd->dem);

			AbsoluteTransformation = *(rd->ParentAbsoluteMatrix) * GetRelativeTransformation();

			UpdateNode();

			if (ChildList.size())
			{
				matrix3dvpos *Save = rd->ParentAbsoluteMatrix;
				rd->ParentAbsoluteMatrix = &AbsoluteTransformation;
				for (std::list<ISceneNode*>::iterator it=ChildList.begin();it!=ChildList.end();it++)
				{
					NodesRendered += (*it)->RegisterNode(rd);
				}
				rd->ParentAbsoluteMatrix = Save;
			}
		}

		return NodesRendered;
	}

	virtual u32 GetNumFaces()
	{
		return 0;
	}

	virtual IDrawableFace *GetFace(u32 Index)
	{
		return NULL;
	}

	virtual void DrawDebugNode(u32 Level)
	{
	    for (u32 i=0;i<Level;i++)
            pLogDebug << " ";

	    pLogDebug << "Node: " << GetName() << ",Position:" << GetPositionStr() << ",Scale:" << GetScaleStr() << ",Rotation:" << GetRotationStr() << ",Visible:" << Visible << endl;

        for (std::list<ISceneNode*>::iterator it=ChildList.begin();it!=ChildList.end();it++)
            (*it)->DrawDebugNode(Level+1);
	}
};

#endif
